<!DOCTYPE html>
<html lang="en">
<head>
    <title>囧么肥事</title>
    <meta charset="UTF-8"/>
    <meta name="viewport"
          content="width=device-width,height=device-height,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta http-equiv="Content-Type" content="text/html;"/>
    <meta name="renderer" content="webkit">
    <!--解决页面加载网络图片的403问题 , 可以让img标签预加载网络图片-->
    <meta name="referrer" content="no-referrer" />

    <!--markdown S -->
    <link rel="stylesheet" type="text/css" href="../../static/css/export_setting_css.css"/>
    <link rel="stylesheet" type="text/css" href="../../static/css/markdown_preview_css.css"/>
    <!--markdown E -->

    <!--left bar tools css E-->
    <link rel="stylesheet" type="text/css" href="../../static/css/nav.css">
    <link rel="stylesheet" type="text/css" href="http://www.jq22.com/jquery/font-awesome.4.6.0.css">
    <!--left bar tools css E-->

    <!-- 滚动条 S -->
    <link rel="stylesheet" href="../../static/css/optiscroll.css">
    <!-- 滚动条 E -->
</head>
<body>
<div id="m-wrapper" class="m-wrapper optiscroll">
    <!-- top 锚点 -->
    <a id="top"></a>

    <!-- left bar tools S -->
    <div class="leftNav-item">
        <ul>
            <li>
                <i class="fa fa-location-arrow"></i>
                <a href="http://jiongmefeishi.gitee.io/monster-blog/" target="_blank" class="rota">个人网站</a>
            </li>

            <li title="Git">
                <i class="fa fa-code-fork" aria-hidden="true"></i>
                <a href="http://jiongmefeishi.gitee.io/monster-blog/" target="_blank" class="rota">Git Page</a>
            </li>

            <li lay-data="img" data-fooc="http://resource.zqtaotao.cn/wx/qrcode/jiongmefeishi.jpg">
                <i class="fa fa-comments-o"></i><a href="javascript:;" class="rota">公众号</a>
            </li>

            <li lay-data="img" data-fooc="http://resource.zqtaotao.cn/weixincode.png">
                <i class="fa fa-money"></i><a href="javascript:;" class="rota">小额打赏</a>
            </li>
            <li title="返回顶部" class="for-top">
                <i class="fa fa-arrow-up"></i>
                <a href="#top" class="rota">去顶部</a>
            </li>
        </ul>
    </div>
    <!-- left bar tools E -->

    <!--content S-->
    <div id="export_content"><div class="output_wrapper" id="output_wrapper_id"><h3 id="h"><span>冰墩墩新鲜出炉，趁热乎赶紧安排！！！</span></h3>
        <p>2022冬奥会震撼开幕，吉祥物 <code>冰墩墩</code> 也🔥了，吸粉无数，无数小伙伴大呼：“<strong>一墩难求</strong>”。</p>
        <p>现在它来了，它来了，它来了！</p>
        <p>程序员 dragonir 开源了冰墩墩3D动画版代码，圆大家「<strong>人手一墩</strong>」的梦想。</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_jpg/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f9N1AFDwd8vjXciaicfd4Frnr0vlmR72KeKFb8Tcw2BuL4gCuHM47NnrRg/640?wx_fmt=jpeg&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <h3 id="h3d"><span>3D 动画冰墩墩</span></h3>
        <p>预览：<a href="https://dragonir.github.io/3d/#/olympic"><strong>3D动画冰墩墩,点击查看</strong></a></p>
        <blockquote>
            <p>声明：本文涉及奥运元素3D模型仅用于个人学习、研究和欣赏，请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。</p>
            <p>原文地址：https://segmentfault.com/a/1190000041363089</p>
        </blockquote>
        <h2 id="h-1"><span>背景</span></h2>
        <p>迎冬奥，一起向未来！<strong>2022冬奥会</strong>马上就要开始了，本文使用 <code>Three.js + React</code> 技术栈，实现冬日和奥运元素，制作了一个充满趣味和纪念意义的冬奥主题 <code>3D</code> 页面。本文涉及到的知识点主要包括：<code>TorusGeometry</code> 圆环面、<code>MeshLambertMaterial</code> 非光泽表面材质、<code>MeshDepthMaterial</code> 深度网格材质、<code>custromMaterial</code> 自定义材质、<code>Points</code> 粒子、<code>PointsMaterial</code> 点材质等。</p>
        <h2 id="h-2"><span>效果</span></h2>
        <p>实现效果如以下 <code>👇</code> 动图所示，页面主要由 <code>2022</code> 冬奥会吉祥物 <code>冰墩墩</code> 、奥运五环、舞动的旗帜 <code>🚩</code>、树木 <code>🌲</code> 以及下雪效果 <code>❄️</code> 等组成。按住鼠标左键移动可以改为相机位置，获得不同视图。</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_gif/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f97Fc3slqRjfiaPG1E7JiaT4zLcMkujmFlDvdsHTyrcia5cE3xeqtqVGAGQ/640?wx_fmt=gif&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <blockquote>
            <p><code>👀</code> 在线预览：https://dragonir.github.io/3d… （部署在 <code>GitHub</code>，加载速度可能会有点慢 <code>😓</code>）</p>
        </blockquote>
        <h2 id="h-3"><span>实现</span></h2>
        <h3 id="h-4"><span>引入资源</span></h3>
        <p>首先引入开发页面所需要的库和外部资源，<code>OrbitControls</code> 用于镜头轨道控制、<code>TWEEN</code> 用于补间动画实现、<code>GLTFLoader</code> 用于加载 <code>glb</code> 或 <code>gltf</code> 格式的 <code>3D</code> 模型、以及一些其他模型、贴图等资源。</p>
        <pre><code class="hljs javascript"><span class="hljs-keyword">import</span>&nbsp;React&nbsp;<span class="hljs-keyword">from</span>&nbsp;<span class="hljs-string">'react'</span>;<br><span class="hljs-keyword">import</span>&nbsp;{&nbsp;OrbitControls&nbsp;}&nbsp;<span class="hljs-keyword">from</span>&nbsp;<span class="hljs-string">"three/examples/jsm/controls/OrbitControls"</span>;<br><span class="hljs-keyword">import</span>&nbsp;{&nbsp;TWEEN&nbsp;}&nbsp;<span class="hljs-keyword">from</span>&nbsp;<span class="hljs-string">"three/examples/jsm/libs/tween.module.min.js"</span>;<br><span class="hljs-keyword">import</span>&nbsp;{&nbsp;GLTFLoader&nbsp;}&nbsp;<span class="hljs-keyword">from</span>&nbsp;<span class="hljs-string">"three/examples/jsm/loaders/GLTFLoader"</span>;<br><span class="hljs-keyword">import</span>&nbsp;bingdundunModel&nbsp;<span class="hljs-keyword">from</span>&nbsp;<span class="hljs-string">'./models/bingdundun.glb'</span>;<br><span class="hljs-comment">//&nbsp;...</span><br></code></pre>
        <h3 id="hdom"><span>页面DOM结构</span></h3>
        <p>页面 <code>DOM</code> 结构非常简单，只有渲染 <code>3D</code> 元素的 <code>#container</code> 容器和显示加载进度的 <code>.olympic_loading</code>元素。</p>
        <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span><br>&nbsp;&nbsp;<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&nbsp;<span class="hljs-attr">id</span>=<span class="hljs-string">"container"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>&nbsp;&nbsp;{this.state.loadingProcess&nbsp;===&nbsp;100&nbsp;?&nbsp;''&nbsp;:&nbsp;(<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&nbsp;<span class="hljs-attr">className</span>=<span class="hljs-string">"olympic_loading"</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&nbsp;<span class="hljs-attr">className</span>=<span class="hljs-string">"box"</span>&gt;</span>{this.state.loadingProcess}&nbsp;%<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>&nbsp;&nbsp;)}<br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre>
        <h3 id="h-5"><span>场景初始化</span></h3>
        <p>初始化渲染容器、场景、相机。关于这部分内容的详细知识点，可以查阅我往期的文章，本文中不再赘述。</p>
        <pre><code class="hljs dart">container&nbsp;=&nbsp;<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'container'</span>);<br>renderer&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.WebGLRenderer({&nbsp;antialias:&nbsp;<span class="hljs-keyword">true</span>&nbsp;});<br>renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);<br>renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth,&nbsp;<span class="hljs-built_in">window</span>.innerHeight);<br>renderer.shadowMap.enabled&nbsp;=&nbsp;<span class="hljs-keyword">true</span>;<br>container.appendChild(renderer.domElement);<br>scene&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Scene();<br>scene.background&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.TextureLoader().load(skyTexture);<br>camera&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.PerspectiveCamera(<span class="hljs-number">60</span>,&nbsp;<span class="hljs-built_in">window</span>.innerWidth&nbsp;/&nbsp;<span class="hljs-built_in">window</span>.innerHeight,&nbsp;<span class="hljs-number">0.1</span>,&nbsp;<span class="hljs-number">1000</span>);<br>camera.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">0</span>,&nbsp;<span class="hljs-number">30</span>,&nbsp;<span class="hljs-number">100</span>);<br>camera.lookAt(<span class="hljs-keyword">new</span>&nbsp;THREE.Vector3(<span class="hljs-number">0</span>,&nbsp;<span class="hljs-number">0</span>,&nbsp;<span class="hljs-number">0</span>));<br></code></pre>
        <h3 id="h-6"><span>添加光源</span></h3>
        <p>本示例中主要添加了两种光源：<code>DirectionalLight</code> 用于产生阴影，调节页面亮度、<code>AmbientLight</code> 用于渲染环境氛围。</p>
        <pre><code class="hljs cs"><span class="hljs-comment">//&nbsp;直射光</span><br><span class="hljs-keyword">const</span>&nbsp;light&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>,&nbsp;<span class="hljs-number">1</span>);<br>light.intensity&nbsp;=&nbsp;<span class="hljs-number">1</span>;<br>light.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">16</span>,&nbsp;<span class="hljs-number">16</span>,&nbsp;<span class="hljs-number">8</span>);<br>light.castShadow&nbsp;=&nbsp;<span class="hljs-literal">true</span>;<br>light.shadow.mapSize.width&nbsp;=&nbsp;<span class="hljs-number">512</span>&nbsp;*&nbsp;<span class="hljs-number">12</span>;<br>light.shadow.mapSize.height&nbsp;=&nbsp;<span class="hljs-number">512</span>&nbsp;*&nbsp;<span class="hljs-number">12</span>;<br>light.shadow.camera.top&nbsp;=&nbsp;<span class="hljs-number">40</span>;<br>light.shadow.camera.bottom&nbsp;=&nbsp;<span class="hljs-number">-40</span>;<br>light.shadow.camera.left&nbsp;=&nbsp;<span class="hljs-number">-40</span>;<br>light.shadow.camera.right&nbsp;=&nbsp;<span class="hljs-number">40</span>;<br>scene.<span class="hljs-keyword">add</span>(light);<br><span class="hljs-comment">//&nbsp;环境光</span><br><span class="hljs-keyword">const</span>&nbsp;ambientLight&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.AmbientLight(<span class="hljs-number">0xcfffff</span>);<br>ambientLight.intensity&nbsp;=&nbsp;<span class="hljs-number">1</span>;<br>scene.<span class="hljs-keyword">add</span>(ambientLight);<br></code></pre>
        <h3 id="h-7"><span>加载进度管理</span></h3>
        <p>使用 <code>THREE.LoadingManager</code> 管理页面模型加载进度，在它的回调函数中执行一些与加载进度相关的方法。本例中的页面加载进度就是在 <code>onProgress</code> 中完成的，当页面加载进度为 <code>100%</code> 时，执行 <code>TWEEN</code> 镜头补间动画。</p>
        <pre><code class="hljs coffeescript">const&nbsp;manager&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.LoadingManager();<br>manager.onStart&nbsp;=&nbsp;<span class="hljs-function"><span class="hljs-params">(url,&nbsp;loaded,&nbsp;total)</span>&nbsp;=&gt;</span>&nbsp;{};<br>manager.onLoad&nbsp;=&nbsp;<span class="hljs-function"><span class="hljs-params">()</span>&nbsp;=&gt;</span>&nbsp;{&nbsp;<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Loading&nbsp;complete!'</span>)};<br>manager.onProgress&nbsp;=&nbsp;<span class="hljs-function"><span class="hljs-params">(url,&nbsp;loaded,&nbsp;total)</span>&nbsp;=&gt;</span>&nbsp;{<br>&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(Math.floor(loaded&nbsp;/&nbsp;total&nbsp;*&nbsp;<span class="hljs-number">100</span>)&nbsp;===&nbsp;<span class="hljs-number">100</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">this</span>.setState({&nbsp;loadingProcess:&nbsp;Math.floor(loaded&nbsp;/&nbsp;total&nbsp;*&nbsp;<span class="hljs-number">100</span>)&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-regexp">//</span>&nbsp;镜头补间动画<br>&nbsp;&nbsp;&nbsp;&nbsp;Animations.animateCamera(camera,&nbsp;controls,&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">0</span>,&nbsp;y:&nbsp;<span class="hljs-number">-1</span>,&nbsp;z:&nbsp;<span class="hljs-number">20</span>&nbsp;},&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">0</span>,&nbsp;y:&nbsp;<span class="hljs-number">0</span>,&nbsp;z:&nbsp;<span class="hljs-number">0</span>&nbsp;},&nbsp;<span class="hljs-number">3600</span>,&nbsp;<span class="hljs-function"><span class="hljs-params">()</span>&nbsp;=&gt;</span>&nbsp;{});<br>&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">this</span>.setState({&nbsp;loadingProcess:&nbsp;Math.floor(loaded&nbsp;/&nbsp;total&nbsp;*&nbsp;<span class="hljs-number">100</span>)&nbsp;});<br>&nbsp;&nbsp;}<br>};<br></code></pre>
        <h3 id="h-8"><span>创建地面</span></h3>
        <p>本示例中凹凸起伏的地面是使用 <code>Blender</code> 构建模型，然后导出 <code>glb</code> 格式加载创建的。当然也可以只使用 <code>Three.js</code> 自带平面网格加凹凸贴图也可以实现类似的效果。使用 <code>Blender</code> 自建模型的优点在于可以自由可视化地调整地面的起伏效果。</p>
        <pre><code class="hljs javascript"><span class="hljs-keyword">var</span>&nbsp;loader&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.GLTFLoader(manager);<br>loader.load(landModel,&nbsp;<span class="hljs-function"><span class="hljs-keyword">function</span>&nbsp;(<span class="hljs-params">mesh</span>)&nbsp;</span>{<br>&nbsp;&nbsp;mesh.scene.traverse(<span class="hljs-function"><span class="hljs-keyword">function</span>&nbsp;(<span class="hljs-params">child</span>)&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.isMesh)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.metalness&nbsp;=&nbsp;<span class="hljs-number">.1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.roughness&nbsp;=&nbsp;<span class="hljs-number">.8</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;地面</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.name&nbsp;===&nbsp;<span class="hljs-string">'Mesh_2'</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.metalness&nbsp;=&nbsp;<span class="hljs-number">.5</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.receiveShadow&nbsp;=&nbsp;<span class="hljs-literal">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;});<br>&nbsp;&nbsp;mesh.scene.rotation.y&nbsp;=&nbsp;<span class="hljs-built_in">Math</span>.PI&nbsp;/&nbsp;<span class="hljs-number">4</span>;<br>&nbsp;&nbsp;mesh.scene.position.set(<span class="hljs-number">15</span>,&nbsp;<span class="hljs-number">-20</span>,&nbsp;<span class="hljs-number">0</span>);<br>&nbsp;&nbsp;mesh.scene.scale.set(<span class="hljs-number">.9</span>,&nbsp;<span class="hljs-number">.9</span>,&nbsp;<span class="hljs-number">.9</span>);<br>&nbsp;&nbsp;land&nbsp;=&nbsp;mesh.scene;<br>&nbsp;&nbsp;scene.add(land);<br>});<br></code></pre>
        <figure><img src="https://img-blog.csdnimg.cn/img_convert/2bf3d60c09c176b5ad676557918c77fe.png" alt="img" title="img"><figcaption>img</figcaption></figure>
        <h3 id="h-9"><span>创建冬奥吉祥物冰墩墩</span></h3>
        <p>现在添加可爱的冬奥会吉祥物熊猫冰墩墩 <code>🐼</code>，冰墩墩同样是使用 <code>glb</code> 格式模型加载的。它的原始模型来源于这里，从这个网站免费现在模型后，原模型是使用 <code>3D max</code> 建的我发现并不能直接用在网页中，需要在 <code>Blender</code> 中转换模型格式，还需要调整调整模型的贴图法线，才能还原渲染图效果。</p>
        <p><strong>原模型</strong>：</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_png/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f9RbsoPFwnpHITibVzGv4dgBk1WiaWamvPkiaJpp9YttXjCqmJ2fIkNQJ9w/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p><strong>冰墩墩贴图</strong>:</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_png/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f9Ku2m8DicshRXURNPO8yBlugtCwjtXptdcibQKmWvamOlTUE1T7OibGgPw/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p><strong>转换成Blender支持的模型，并在Blender中调整模型贴图法线、并添加贴图</strong>:</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_png/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f9c0kVEicUDdoJL6RYdvhftuVlRgLvvgmO0l47Fz7ibLSnPqkvYF8NfbFQ/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p><strong>导出glb格式</strong>:</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_png/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f95ehlTMNyEQe9mav3cIoHkca1O1iapfxX6VicwrmITrjCSROEGF9Ssjhg/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <blockquote>
            <p><code>📖</code> 在 <code>Blender</code> 中给模型添加贴图教程传送门：在Blender中怎么给模型贴图</p>
        </blockquote>
        <p>仔细观察<code>冰墩墩 🐼</code>可以发现，它的外面有一层<strong>透明塑料或玻璃质感外壳</strong>，这个效果可以通过修改模型的透明度、金属度、粗糙度等材质参数实现，最后就可以渲染出如 <code>👆 banner图</code> 所示的那种效果，具体如以下代码所示。</p>
        <pre><code class="hljs dart">loader.load(bingdundunModel,&nbsp;mesh&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;mesh.scene.traverse(child&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.isMesh)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;内部</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.name&nbsp;===&nbsp;<span class="hljs-string">'oldtiger001'</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.metalness&nbsp;=&nbsp;<span class="hljs-number">.5</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.roughness&nbsp;=&nbsp;<span class="hljs-number">.8</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;半透明外壳</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.name&nbsp;===&nbsp;<span class="hljs-string">'oldtiger002'</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.transparent&nbsp;=&nbsp;<span class="hljs-keyword">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.opacity&nbsp;=&nbsp;<span class="hljs-number">.5</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.metalness&nbsp;=&nbsp;<span class="hljs-number">.2</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.roughness&nbsp;=&nbsp;<span class="hljs-number">0</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.refractionRatio&nbsp;=&nbsp;<span class="hljs-number">1</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.castShadow&nbsp;=&nbsp;<span class="hljs-keyword">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;});<br>&nbsp;&nbsp;mesh.scene.rotation.y&nbsp;=&nbsp;Math.PI&nbsp;/&nbsp;<span class="hljs-number">24</span>;<br>&nbsp;&nbsp;mesh.scene.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">-8</span>,&nbsp;<span class="hljs-number">-12</span>,&nbsp;<span class="hljs-number">0</span>);<br>&nbsp;&nbsp;mesh.scene.scale.<span class="hljs-keyword">set</span>(<span class="hljs-number">24</span>,&nbsp;<span class="hljs-number">24</span>,&nbsp;<span class="hljs-number">24</span>);<br>&nbsp;&nbsp;scene.add(mesh.scene);<br>});<br></code></pre>
        <h3 id="h-10"><span>创建奥运五环</span></h3>
        <p>奥运五环由基础几何模型圆环面 <code>TorusGeometry</code> 来实现，创建五个圆环面，并调整它们的材质颜色和位置来构成<strong>蓝黑红黄绿</strong>顺序的五环结构。五环材质使用的是 <code>MeshLambertMaterial</code>。</p>
        <pre><code class="hljs cs"><span class="hljs-keyword">const</span>&nbsp;fiveCycles&nbsp;=&nbsp;[<br>&nbsp;&nbsp;{&nbsp;key:&nbsp;<span class="hljs-string">'cycle_0'</span>,&nbsp;color:&nbsp;<span class="hljs-number">0x0885c2</span>,&nbsp;position:&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">-250</span>,&nbsp;y:&nbsp;<span class="hljs-number">0</span>,&nbsp;z:&nbsp;<span class="hljs-number">0</span>&nbsp;}},<br>&nbsp;&nbsp;{&nbsp;key:&nbsp;<span class="hljs-string">'cycle_1'</span>,&nbsp;color:&nbsp;<span class="hljs-number">0x000000</span>,&nbsp;position:&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">-10</span>,&nbsp;y:&nbsp;<span class="hljs-number">0</span>,&nbsp;z:&nbsp;<span class="hljs-number">5</span>&nbsp;}},<br>&nbsp;&nbsp;{&nbsp;key:&nbsp;<span class="hljs-string">'cycle_2'</span>,&nbsp;color:&nbsp;<span class="hljs-number">0xed334e</span>,&nbsp;position:&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">230</span>,&nbsp;y:&nbsp;<span class="hljs-number">0</span>,&nbsp;z:&nbsp;<span class="hljs-number">0</span>&nbsp;}},<br>&nbsp;&nbsp;{&nbsp;key:&nbsp;<span class="hljs-string">'cycle_3'</span>,&nbsp;color:&nbsp;<span class="hljs-number">0xfbb132</span>,&nbsp;position:&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">-125</span>,&nbsp;y:&nbsp;<span class="hljs-number">-100</span>,&nbsp;z:&nbsp;<span class="hljs-number">-5</span>&nbsp;}},<br>&nbsp;&nbsp;{&nbsp;key:&nbsp;<span class="hljs-string">'cycle_4'</span>,&nbsp;color:&nbsp;<span class="hljs-number">0x1c8b3c</span>,&nbsp;position:&nbsp;{&nbsp;x:&nbsp;<span class="hljs-number">115</span>,&nbsp;y:&nbsp;<span class="hljs-number">-100</span>,&nbsp;z:&nbsp;<span class="hljs-number">10</span>&nbsp;}}<br>];<br>fiveCycles.map(item&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;cycleMesh&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Mesh(<span class="hljs-keyword">new</span>&nbsp;THREE.TorusGeometry(<span class="hljs-number">100</span>,&nbsp;<span class="hljs-number">10</span>,&nbsp;<span class="hljs-number">10</span>,&nbsp;<span class="hljs-number">50</span>),&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.MeshLambertMaterial({<br>&nbsp;&nbsp;&nbsp;&nbsp;color:&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Color(item.color),<br>&nbsp;&nbsp;&nbsp;&nbsp;side:&nbsp;THREE.DoubleSide<br>&nbsp;&nbsp;}));<br>&nbsp;&nbsp;cycleMesh.castShadow&nbsp;=&nbsp;<span class="hljs-literal">true</span>;<br>&nbsp;&nbsp;cycleMesh.position.<span class="hljs-keyword">set</span>(item.position.x,&nbsp;item.position.y,&nbsp;item.position.z);<br>&nbsp;&nbsp;meshes.push(cycleMesh);<br>&nbsp;&nbsp;fiveCyclesGroup.<span class="hljs-keyword">add</span>(cycleMesh);<br>});<br>fiveCyclesGroup.scale.<span class="hljs-keyword">set</span>(<span class="hljs-number">.036</span>,&nbsp;<span class="hljs-number">.036</span>,&nbsp;<span class="hljs-number">.036</span>);<br>fiveCyclesGroup.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">0</span>,&nbsp;<span class="hljs-number">10</span>,&nbsp;<span class="hljs-number">-8</span>);<br>scene.<span class="hljs-keyword">add</span>(fiveCyclesGroup);<br></code></pre>
        <p><code>💡</code> TorusGeometry 圆环面</p>
        <p><code>TorusGeometry</code> 一个用于生成圆环几何体的类。</p>
        <p><strong>构造函数</strong>：</p>
        <pre><code class="hljs kotlin">TorusGeometry(radius:&nbsp;<span class="hljs-built_in">Float</span>,&nbsp;tube:&nbsp;<span class="hljs-built_in">Float</span>,&nbsp;radialSegments:&nbsp;Integer,&nbsp;tubularSegments:&nbsp;Integer,&nbsp;arc:&nbsp;<span class="hljs-built_in">Float</span>)<br></code></pre>
        <ul>
            <li><code>radius</code>：圆环的半径，从圆环的中心到管道（横截面）的中心。默认值是 <code>1</code>。</li>
            <li><code>tube</code>：管道的半径，默认值为 <code>0.4</code>。</li>
            <li><code>radialSegments</code>：圆环的分段数，默认值为 <code>8</code>。</li>
            <li><code>tubularSegments</code>：管道的分段数，默认值为 <code>6</code>。</li>
            <li><code>arc</code>：圆环的圆心角（单位是弧度），默认值为 <code>Math.PI * 2</code>。</li>
        </ul>
        <p><code>💡</code> MeshLambertMaterial 非光泽表面材质</p>
        <p>一种非光泽表面的材质，没有镜面高光。该材质使用基于非物理的 <code>Lambertian</code> 模型来计算反射率。这可以很好地模拟一些表面（例如未经处理的木材或石材），但不能模拟具有镜面高光的光泽表面（例如涂漆木材）。</p>
        <p><strong>构造函数</strong>：</p>
        <pre><code class="hljs javascript">MeshLambertMaterial(parameters&nbsp;:&nbsp;<span class="hljs-built_in">Object</span>)<br></code></pre>
        <ul>
            <li><code>parameters</code>：（可选）用于定义材质外观的对象，具有一个或多个属性。材质的任何属性都可以从此处传入。</li>
        </ul>
        <h3 id="h-11"><span>创建旗帜</span></h3>
        <p>旗面模型是从sketchfab下载的，还需要一个旗杆，可以在 <code>Blender</code>中添加了一个柱状立方体，并调整好合适的长宽高和旗面结合起来。</p>
        <figure><img src="https://img-blog.csdnimg.cn/img_convert/8c1e74fce9e7f1ecf13a8373bd5b5735.png" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p><strong>旗面贴图</strong>：</p>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_png/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f9vvrqickUSZYGTLp5GBxlp9EoF1XFQr8PMPoHiclHZFmYzgQcfgIricQzw/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p>旗面添加了动画，需要在代码中执行动画帧播放。</p>
        <pre><code class="hljs cs">loader.load(flagModel,&nbsp;mesh&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;mesh.scene.traverse(child&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.isMesh)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.castShadow&nbsp;=&nbsp;<span class="hljs-literal">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;旗帜</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.name&nbsp;===&nbsp;<span class="hljs-string">'mesh_0001'</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.metalness&nbsp;=&nbsp;<span class="hljs-number">.1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.roughness&nbsp;=&nbsp;<span class="hljs-number">.1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.map&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.TextureLoader().load(flagTexture);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;旗杆</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.name&nbsp;===&nbsp;<span class="hljs-string">'柱体'</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.metalness&nbsp;=&nbsp;<span class="hljs-number">.6</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.roughness&nbsp;=&nbsp;<span class="hljs-number">0</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.refractionRatio&nbsp;=&nbsp;<span class="hljs-number">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material.color&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Color(<span class="hljs-number">0xeeeeee</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;});<br>&nbsp;&nbsp;mesh.scene.rotation.y&nbsp;=&nbsp;Math.PI&nbsp;/&nbsp;<span class="hljs-number">24</span>;<br>&nbsp;&nbsp;mesh.scene.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">2</span>,&nbsp;<span class="hljs-number">-7</span>,&nbsp;<span class="hljs-number">-1</span>);<br>&nbsp;&nbsp;mesh.scene.scale.<span class="hljs-keyword">set</span>(<span class="hljs-number">4</span>,&nbsp;<span class="hljs-number">4</span>,&nbsp;<span class="hljs-number">4</span>);<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;动画</span><br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;meshAnimation&nbsp;=&nbsp;mesh.animations[<span class="hljs-number">0</span>];<br>&nbsp;&nbsp;mixer&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.AnimationMixer(mesh.scene);<br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;animationClip&nbsp;=&nbsp;meshAnimation;<br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;clipAction&nbsp;=&nbsp;mixer.clipAction(animationClip).play();<br>&nbsp;&nbsp;animationClip&nbsp;=&nbsp;clipAction.getClip();<br>&nbsp;&nbsp;scene.<span class="hljs-keyword">add</span>(mesh.scene);<br>});<br></code></pre>
        <h3 id="h-12"><span>创建树木</span></h3>
        <p>为了充实画面，营造冬日氛围，于是就添加了几棵松树 <code>🌲</code> 作为装饰。添加松树的时候用到一个技巧非常重要：我们知道因为树的模型非常复杂，有非常多的面数，面数太多会降低页面性能，造成卡顿。本文中使用两个如下图 <code>👇</code> 所示的两个交叉的面来作为树的基座，这样的话树只有两个面数，使用这个技巧可以和大程度上优化页面性能，而且树 <code>🌲</code> 的样子看起来也是有 <code>3D</code> 感的。</p>
        <figure><img src="https://img-blog.csdnimg.cn/img_convert/e1d82c0681c0b4087eec537b769c66d5.png" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p><strong>材质贴图</strong>：</p>
        <figure><img src="https://img-blog.csdnimg.cn/img_convert/f215308612199d83194d7924febb474e.png" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p>为了使树<strong>只在贴图透明部分透明、其他地方不透明</strong>，并且可以<strong>产生树状阴影而不是长方体阴影</strong>，需要给树模型添加如下 <code>MeshPhysicalMaterial</code>、<code>MeshDepthMaterial</code> 两种材质，两种材质使用同样的纹理贴图，其中 <code>MeshDepthMaterial</code> 添加到模型的 <code>custromMaterial</code> 属性上。</p>
        <pre><code class="hljs cs">&nbsp;<span class="hljs-keyword">let</span>&nbsp;treeMaterial&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.MeshPhysicalMaterial({<br>&nbsp;&nbsp;map:&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.TextureLoader().load(treeTexture),<br>&nbsp;&nbsp;transparent:&nbsp;<span class="hljs-literal">true</span>,<br>&nbsp;&nbsp;side:&nbsp;THREE.DoubleSide,<br>&nbsp;&nbsp;metalness:&nbsp;<span class="hljs-number">.2</span>,<br>&nbsp;&nbsp;roughness:&nbsp;<span class="hljs-number">.8</span>,<br>&nbsp;&nbsp;depthTest:&nbsp;<span class="hljs-literal">true</span>,<br>&nbsp;&nbsp;depthWrite:&nbsp;<span class="hljs-literal">false</span>,<br>&nbsp;&nbsp;skinning:&nbsp;<span class="hljs-literal">false</span>,<br>&nbsp;&nbsp;fog:&nbsp;<span class="hljs-literal">false</span>,<br>&nbsp;&nbsp;reflectivity:&nbsp;<span class="hljs-number">0.1</span>,<br>&nbsp;&nbsp;refractionRatio:&nbsp;<span class="hljs-number">0</span>,<br>});<br><span class="hljs-keyword">let</span>&nbsp;treeCustomDepthMaterial&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.MeshDepthMaterial({<br>&nbsp;&nbsp;depthPacking:&nbsp;THREE.RGBADepthPacking,<br>&nbsp;&nbsp;map:&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.TextureLoader().load(treeTexture),<br>&nbsp;&nbsp;alphaTest:&nbsp;<span class="hljs-number">0.5</span><br>});<br>loader.load(treeModel,&nbsp;mesh&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;mesh.scene.traverse(child&nbsp;=&gt;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(child.isMesh)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.material&nbsp;=&nbsp;treeMaterial;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;child.custromMaterial&nbsp;=&nbsp;treeCustomDepthMaterial;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;});<br>&nbsp;&nbsp;mesh.scene.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">14</span>,&nbsp;<span class="hljs-number">-9</span>,&nbsp;<span class="hljs-number">0</span>);<br>&nbsp;&nbsp;mesh.scene.scale.<span class="hljs-keyword">set</span>(<span class="hljs-number">16</span>,&nbsp;<span class="hljs-number">16</span>,&nbsp;<span class="hljs-number">16</span>);<br>&nbsp;&nbsp;scene.<span class="hljs-keyword">add</span>(mesh.scene);<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;克隆另两棵树</span><br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;tree2&nbsp;=&nbsp;mesh.scene.clone();<br>&nbsp;&nbsp;tree2.position.<span class="hljs-keyword">set</span>(<span class="hljs-number">10</span>,&nbsp;<span class="hljs-number">-8</span>,&nbsp;<span class="hljs-number">-15</span>);<br>&nbsp;&nbsp;tree2.scale.<span class="hljs-keyword">set</span>(<span class="hljs-number">18</span>,&nbsp;<span class="hljs-number">18</span>,&nbsp;<span class="hljs-number">18</span>);<br>&nbsp;&nbsp;scene.<span class="hljs-keyword">add</span>(tree2)<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;...</span><br>});<br></code></pre>
        <p>实现效果也可以从 <code>👆</code> 上面 <code>Banner</code> 图中可以看到，为了画面更好看，我取消了树的阴影显示。</p>
        <blockquote>
            <p><code>📌</code> 在 <code>3D</code> 功能开发中，一些不重要的装饰模型都可以采取这种策略来优化。</p>
        </blockquote>
        <p><code>💡</code> MeshDepthMaterial 深度网格材质</p>
        <p>一种按深度绘制几何体的材质。深度基于相机远近平面，白色最近，黑色最远。</p>
        <p><strong>构造函数</strong>：</p>
        <pre><code class="hljs javascript">MeshDepthMaterial(parameters:&nbsp;<span class="hljs-built_in">Object</span>)<br></code></pre>
        <ul>
            <li><code>parameters</code>：（可选）用于定义材质外观的对象，具有一个或多个属性。材质的任何属性都可以从此处传入。</li>
        </ul>
        <p><strong>特殊属性</strong>：</p>
        <ul>
            <li><code>.depthPacking[Constant]</code>：<code>depth packing</code> 的编码。默认为 <code>BasicDepthPacking</code>。</li>
            <li><code>.displacementMap[Texture]</code>：位移贴图会影响网格顶点的位置，与仅影响材质的光照和阴影的其他贴图不同，移位的顶点可以投射阴影，阻挡其他对象，以及充当真实的几何体。</li>
            <li><code>.displacementScale[Float]</code>：位移贴图对网格的影响程度（黑色是无位移，白色是最大位移）。如果没有设置位移贴图，则不会应用此值。默认值为 <code>1</code>。</li>
            <li><code>.displacementBias[Float]</code>：位移贴图在网格顶点上的偏移量。如果没有设置位移贴图，则不会应用此值。默认值为 <code>0</code>。</li>
        </ul>
        <p><code>💡</code> custromMaterial 自定义材质</p>
        <p>给网格添加 <code>custromMaterial</code> 自定义材质属性，可以实现透明外围 <code>png</code> 图片贴图的内容区域阴影。</p>
        <h3 id="h-13"><span>创建雪花</span></h3>
        <p>创建雪花 <code>❄️</code>，就要用到<strong>粒子知识</strong>。<code>THREE.Points</code> 是用来创建点的类，也用来批量管理粒子。本例中创建了 <code>1500</code> 个雪花粒子，并为它们设置了限定三维空间的随机坐标及横向和竖向的随机移动速度。</p>
        <pre><code class="hljs javascript"><span class="hljs-comment">//&nbsp;雪花贴图</span><br><span class="hljs-keyword">let</span>&nbsp;texture&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.TextureLoader().load(snowTexture);<br><span class="hljs-keyword">let</span>&nbsp;geometry&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Geometry();<br><span class="hljs-keyword">let</span>&nbsp;range&nbsp;=&nbsp;<span class="hljs-number">100</span>;<br><span class="hljs-keyword">let</span>&nbsp;pointsMaterial&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.PointsMaterial({<br>&nbsp;&nbsp;<span class="hljs-attr">size</span>:&nbsp;<span class="hljs-number">1</span>,<br>&nbsp;&nbsp;<span class="hljs-attr">transparent</span>:&nbsp;<span class="hljs-literal">true</span>,<br>&nbsp;&nbsp;<span class="hljs-attr">opacity</span>:&nbsp;<span class="hljs-number">0.8</span>,<br>&nbsp;&nbsp;<span class="hljs-attr">map</span>:&nbsp;texture,<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;背景融合</span><br>&nbsp;&nbsp;blending:&nbsp;THREE.AdditiveBlending,<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;景深衰弱</span><br>&nbsp;&nbsp;sizeAttenuation:&nbsp;<span class="hljs-literal">true</span>,<br>&nbsp;&nbsp;<span class="hljs-attr">depthTest</span>:&nbsp;<span class="hljs-literal">false</span><br>});<br><span class="hljs-keyword">for</span>&nbsp;(<span class="hljs-keyword">let</span>&nbsp;i&nbsp;=&nbsp;<span class="hljs-number">0</span>;&nbsp;i&nbsp;&lt;&nbsp;<span class="hljs-number">1500</span>;&nbsp;i++)&nbsp;{<br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;vertice&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Vector3(<span class="hljs-built_in">Math</span>.random()&nbsp;*&nbsp;range&nbsp;-&nbsp;range&nbsp;/&nbsp;<span class="hljs-number">2</span>,&nbsp;<span class="hljs-built_in">Math</span>.random()&nbsp;*&nbsp;range&nbsp;*&nbsp;<span class="hljs-number">1.5</span>,&nbsp;<span class="hljs-built_in">Math</span>.random()&nbsp;*&nbsp;range&nbsp;-&nbsp;range&nbsp;/&nbsp;<span class="hljs-number">2</span>);<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;纵向移速</span><br>&nbsp;&nbsp;vertice.velocityY&nbsp;=&nbsp;<span class="hljs-number">0.1</span>&nbsp;+&nbsp;<span class="hljs-built_in">Math</span>.random()&nbsp;/&nbsp;<span class="hljs-number">3</span>;<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;横向移速</span><br>&nbsp;&nbsp;vertice.velocityX&nbsp;=&nbsp;(<span class="hljs-built_in">Math</span>.random()&nbsp;-&nbsp;<span class="hljs-number">0.5</span>)&nbsp;/&nbsp;<span class="hljs-number">3</span>;<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;加入到几何</span><br>&nbsp;&nbsp;geometry.vertices.push(vertice);<br>}<br>geometry.center();<br>points&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;THREE.Points(geometry,&nbsp;pointsMaterial);<br>points.position.y&nbsp;=&nbsp;<span class="hljs-number">-30</span>;<br>scene.add(points);<br></code></pre>
        <p><code>💡</code> Points 粒子</p>
        <p><code>Three.js</code> 中，雨 <code>🌧️</code>、雪 <code>❄️</code>、云 <code>☁️</code>、星辰 <code>✨</code> 等生活中常见的粒子都可以使用 <code>Points</code> 来模拟实现。</p>
        <p><strong>构造函数</strong>：</p>
        <pre><code class="hljs coffeescript"><span class="hljs-keyword">new</span>&nbsp;THREE.Points(geometry,&nbsp;material);<br></code></pre>
        <ul>
            <li><span>构造函数可以接受两个参数，一个几何体和一个材质，几何体参数用来制定粒子的位置坐标，材质参数用来格式化粒子；</span></li>
            <li>可以基于简单几何体对象如 <code>BoxGeometry</code>、<code>SphereGeometry</code>等作为粒子系统的参数；</li>
            <li><span>一般来讲，需要自己指定顶点来确定粒子的位置。</span></li>
        </ul>
        <p><code>💡</code> PointsMaterial 点材质</p>
        <p>通过 <code>THREE.PointsMaterial</code> 可以设置粒子的属性参数，是 <code>Points</code> 使用的默认材质。</p>
        <p><strong>构造函数</strong>：</p>
        <pre><code class="hljs javascript">PointsMaterial(parameters&nbsp;:&nbsp;<span class="hljs-built_in">Object</span>)<br></code></pre>
        <ul>
            <li><code>parameters</code>：（可选）用于定义材质外观的对象，具有一个或多个属性。材质的任何属性都可以从此处传入。</li>
        </ul>
        <p><code>💡</code> 材质属性 .blending</p>
        <p>材质的<code>.blending</code> 属性主要控制纹理融合的叠加方式，<code>.blending</code> 属性的值包括：</p>
        <ul>
            <li><code>THREE.NormalBlending</code>：默认值</li>
            <li><code>THREE.AdditiveBlending</code>：加法融合模式</li>
            <li><code>THREE.SubtractiveBlending</code>：减法融合模式</li>
            <li><code>THREE.MultiplyBlending</code>：乘法融合模式</li>
            <li><code>THREE.CustomBlending</code>：自定义融合模式，与 <code>.blendSrc</code>, <code>.blendDst</code> 或 <code>.blendEquation</code> 属性组合使用</li>
        </ul>
        <p><code>💡</code> 材质属性 .sizeAttenuation</p>
        <p>粒子的大小是否会被相机深度衰减，默认为 <code>true</code>（仅限透视相机）。</p>
        <p><code>💡</code> Three.js 向量</p>
        <p>几维向量就有几个分量，二维向量 <code>Vector2</code> 有 <code>x</code> 和 <code>y</code> 两个分量，三维向量 <code>Vector3</code> 有<code>x</code>、<code>y</code>、<code>z</code> 三个分量，四维向量 <code>Vector4</code> 有 <code>x</code>、<code>y</code>、<code>z</code>、<code>w</code> 四个分量。</p>
        <p><strong>相关API</strong>：</p>
        <ul>
            <li><code>Vector2</code>：二维向量</li>
            <li><code>Vector3</code>：三维向量</li>
            <li><code>Vector4</code>：四维向量</li>
        </ul>
        <h3 id="h-14"><span>镜头控制、缩放适配、动画</span></h3>
        <pre><code class="hljs javascript">controls&nbsp;=&nbsp;<span class="hljs-keyword">new</span>&nbsp;OrbitControls(camera,&nbsp;renderer.domElement);<br>controls.target.set(<span class="hljs-number">0</span>,&nbsp;<span class="hljs-number">0</span>,&nbsp;<span class="hljs-number">0</span>);<br>controls.enableDamping&nbsp;=&nbsp;<span class="hljs-literal">true</span>;<br><span class="hljs-comment">//&nbsp;禁用平移</span><br>controls.enablePan&nbsp;=&nbsp;<span class="hljs-literal">false</span>;<br><span class="hljs-comment">//&nbsp;禁用缩放</span><br>controls.enableZoom&nbsp;=&nbsp;<span class="hljs-literal">false</span>;<br><span class="hljs-comment">//&nbsp;垂直旋转角度限制</span><br>controls.minPolarAngle&nbsp;=&nbsp;<span class="hljs-number">1.4</span>;<br>controls.maxPolarAngle&nbsp;=&nbsp;<span class="hljs-number">1.8</span>;<br><span class="hljs-comment">//&nbsp;水平旋转角度限制</span><br>controls.minAzimuthAngle&nbsp;=&nbsp;<span class="hljs-number">-.6</span>;<br>controls.maxAzimuthAngle&nbsp;=&nbsp;<span class="hljs-number">.6</span>;<br><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>,&nbsp;()&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;camera.aspect&nbsp;=&nbsp;<span class="hljs-built_in">window</span>.innerWidth&nbsp;/&nbsp;<span class="hljs-built_in">window</span>.innerHeight;<br>&nbsp;&nbsp;camera.updateProjectionMatrix();<br>&nbsp;&nbsp;renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth,&nbsp;<span class="hljs-built_in">window</span>.innerHeight);<br>},&nbsp;<span class="hljs-literal">false</span>);<br><span class="hljs-function"><span class="hljs-keyword">function</span>&nbsp;<span class="hljs-title">animate</span>(<span class="hljs-params"></span>)&nbsp;</span>{<br>&nbsp;&nbsp;requestAnimationFrame(animate);<br>&nbsp;&nbsp;renderer.render(scene,&nbsp;camera);<br>&nbsp;&nbsp;controls&nbsp;&amp;&amp;&nbsp;controls.update();<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;旗帜动画更新</span><br>&nbsp;&nbsp;mixer&nbsp;&amp;&amp;&nbsp;mixer.update(<span class="hljs-keyword">new</span>&nbsp;THREE.Clock().getDelta());<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;镜头动画</span><br>&nbsp;&nbsp;TWEEN&nbsp;&amp;&amp;&nbsp;TWEEN.update();<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;五环自转</span><br>&nbsp;&nbsp;fiveCyclesGroup&nbsp;&amp;&amp;&nbsp;(fiveCyclesGroup.rotation.y&nbsp;+=&nbsp;<span class="hljs-number">.01</span>);<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;顶点变动之后需要更新，否则无法实现雨滴特效</span><br>&nbsp;&nbsp;points.geometry.verticesNeedUpdate&nbsp;=&nbsp;<span class="hljs-literal">true</span>;<br>&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;雪花动画更新</span><br>&nbsp;&nbsp;<span class="hljs-keyword">let</span>&nbsp;vertices&nbsp;=&nbsp;points.geometry.vertices;<br>&nbsp;&nbsp;vertices.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>&nbsp;(<span class="hljs-params">v</span>)&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;v.y&nbsp;=&nbsp;v.y&nbsp;-&nbsp;(v.velocityY);<br>&nbsp;&nbsp;&nbsp;&nbsp;v.x&nbsp;=&nbsp;v.x&nbsp;-&nbsp;(v.velocityX);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(v.y&nbsp;&lt;=&nbsp;<span class="hljs-number">0</span>)&nbsp;v.y&nbsp;=&nbsp;<span class="hljs-number">60</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span>&nbsp;(v.x&nbsp;&lt;=&nbsp;<span class="hljs-number">-20</span>&nbsp;||&nbsp;v.x&nbsp;&gt;=&nbsp;<span class="hljs-number">20</span>)&nbsp;v.velocityX&nbsp;=&nbsp;v.velocityX&nbsp;*&nbsp;<span class="hljs-number">-1</span>;<br>&nbsp;&nbsp;});<br>}<br></code></pre>
        <blockquote>
            <p><code>🔗</code> 完整代码：https://github.com/dragonir/3…</p>
        </blockquote>
        <h2 id="h-15"><span>总结</span></h2>
        <p><code>💡</code> 本文中主要包含的新知识点包括：</p>
        <ul>
            <li><code>TorusGeometry</code> 圆环面</li>
            <li><code>MeshLambertMaterial</code> 非光泽表面材质</li>
            <li><code>MeshDepthMaterial</code> 深度网格材质</li>
            <li><code>custromMaterial</code> 自定义材质</li>
            <li><code>Points</code> 粒子</li>
            <li><code>PointsMaterial</code> 点材质</li>
            <li>材质属性 <code>.blending</code>、<code>.sizeAttenuation</code></li>
            <li><code>Three.js</code> 向量</li>
        </ul>
        <p><strong>进一步优化的空间</strong>:</p>
        <ul>
            <li><span>添加更多的交互功能、界面样式进一步优化；</span></li>
            <li><span>吉祥物冰墩墩添加骨骼动画，并可以通过鼠标和键盘控制其移动和交互。</span></li>
        </ul>
        <p><strong>下期预告</strong>：</p>
        <ul>
            <li>《<code>Metahuman</code>元人类！<code>Three.js</code>人像优化》</li>
        </ul>
        <blockquote>
            <p>想了解场景初始化、光照、阴影、基础几何体、网格、材质及其他 <code>Three.js</code> 的相关知识，可阅读我往期文章。如果觉得文章对你有帮助，不要忘了<strong>一键三连哦 👍</strong>。</p>
        </blockquote>
        <h2 id="h-16"><span>附录</span></h2>
        <ul>
            <li><span>[1]. 1000粉！使用Three.js制作一个专属3D奖牌 🥇</span></li>
            <li><span>[2]. Three.js 实现虎年春节3D创意页面</span></li>
            <li><span>[3]. Three.js 实现脸书元宇宙3D动态Logo</span></li>
            <li><span>[4]. Three.js 实现3D全景侦探小游戏</span></li>
            <li><span>[5]. Three.js实现炫酷的酸性风格3D页面</span></li>
            <li><span>[6]. 3dx模型转换为blender支持格式</span></li>
        </ul>
        <figure><img src="https://mmbiz.qpic.cn/mmbiz_png/2GfhQ2H7oTFHRpkZjEnHL93fWofsg5f9MwBnAdDsb8Y01fIkgbdSl2lheWic6I4WGoINVHgPzZsW5JW3gRbibw6A/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="img" title="img"><figcaption>img</figcaption></figure>
        <p>《囧么肥事历史》</p>
        <p>推荐休闲阅读：<a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg3NjU0NDE4NQ==&amp;action=getalbum&amp;album_id=2218140423993212933#wechat_redirect">《囧么肥事》</a></p></div></div>
    <!--content E-->
</div>



</body>
<!--left bar tools js S-->
<script src="http://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<script type="text/javascript" src="../../static/js/nav.js"></script>
<!--left bar tools js E-->

<!-- 滚动条 S -->
<script type="text/javascript" src="../../static/js/optiscroll.js"></script>
<!-- 滚动条 E -->

<!--commonjs S-->
<script type="text/javascript" src="../../static/js/common.js"></script>
<!--commonjs E-->

</html>
